/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package databook;

import databook.controller.PropertyListener;
import databook.controller.PropertyEvent;
import databook.controller.Props;
import databook.controller.DBRangeSlider;
import databook.controller.*;

import prefuse.Visualization;
import prefuse.Display;
import prefuse.Constants;
import prefuse.data.Table;
import prefuse.data.Edge;
import prefuse.data.Node;
import prefuse.render.PolygonRenderer;
import prefuse.render.RendererFactory;
import prefuse.render.Renderer;
import prefuse.render.AxisRenderer;
import prefuse.action.assignment.ColorAction;
import prefuse.util.ColorLib;
import prefuse.data.query.NumberRangeModel;
import prefuse.data.query.ObjectRangeModel;
import prefuse.visual.VisualItem;
import prefuse.visual.VisualTable;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.layout.StackedAreaChart;
import prefuse.action.layout.AxisLabelLayout;
import prefuse.util.PrefuseLib;
import prefuse.controls.ToolTipControl;
import prefuse.controls.Control;
import prefuse.controls.ControlAdapter;

import java.awt.event.*;
//import java.util.HashMap;

import javax.swing.ToolTipManager;

/**
 *
 * @author zach
 */
public class NodeVisualization extends Visualization implements PropertyListener {
    private String[] types;
    private NumberRangeModel[] ranges;
    private AxisLabelLayout[] xranges;
    private Display[] displays;
    private StackedAreaChart[] stacks;
    
    private int WIDTH = 200;
    private int HEIGHT = 125;
    
    public NodeVisualization(String[] types) {
        super();
        this.types = types;
        ranges = new NumberRangeModel[types.length];
        xranges = new AxisLabelLayout[types.length];
        displays = new Display[types.length];
        stacks = new StackedAreaChart[types.length];
        init();
        setYears();
        Props.addListener(new String[] {Props.SELECTED_ITEM,Props.CLICKED_EDGE},this);
        Props.addListener(new String[] {Props.CURRENT_RANGE_MAX,Props.CURRENT_RANGE_MIN},
                new PropertyListener() {
                    public void propertyChanged(PropertyEvent evt) {
                        setYears();
                    }
                });
        //Props.addListener(Props.CLICKED_EDGE,this);
    }
    
    public Display[] getDisplays() {
        return displays;
    }
    
    public void init() {
        // Sets a custom RendererFactory which returns an axis renderer for
        // the axis layouts and a poly renderer for the stacked graphs.
        setRendererFactory(new RendererFactory() {
            Renderer polyR = new PolygonRenderer(); 
            Renderer arY = new AxisRenderer(Constants.RIGHT, Constants.TOP);
            Renderer arX = new AxisRenderer(Constants.CENTER, Constants.FAR_BOTTOM);
            
            public Renderer getRenderer(VisualItem item) {
                return ((item.getGroup().indexOf("_Y") != -1) || (item.getGroup().indexOf("_X") != -1)) ? arY : polyR;
            }
        });

        for (int i = 0; i < types.length; i++) {
            // Each display only displays certain graphs
            Display d = new Display(this,"INGROUP('" + types[i] + "') OR INGROUP('" + types[i] + "_Y') OR INGROUP('" + types[i] + "_X')");
            
            // Bring tool tips up immidiately.  Is annoying for the main graph, but I feel like is helpful
            // for the stack graphs
            ToolTipManager.sharedInstance().setInitialDelay(0);
            
            //Customize this  class to get a different tool tip
            //d.addControlListener(new ToolTipControl(new String[] {"VALUE"}));
            d.addControlListener(new CustomToolController(WIDTH));
            /*d.addControlListener(new ControlAdapter() {
                public void mouseMoved(MouseEvent evt) {
                    System.out.println(evt.getX() + " " + evt.getY());
                }
                
            });/**/
            
            
            d.setSize(WIDTH,HEIGHT);
            displays[i] = d;
            stacks[i] = new StackedAreaChart(types[i],VisualItem.POLYGON,new String[0]);
            ranges[i] = new NumberRangeModel(0,HEIGHT,0,HEIGHT);
            xranges[i] = new AxisLabelLayout(types[i] + "_X",Constants.X_AXIS,new ObjectRangeModel(new Object[0]));
            xranges[i].setSpacing(2);
            
            
            ColorAction stroke = new ColorAction(types[i], VisualItem.STROKECOLOR,ColorLib.gray(0)); 
            ColorAction fill = new ColorAction(types[i], VisualItem.FILLCOLOR) {
                                        public int getColor(VisualItem item) {
                                            return Props.getColor(item.getString("PROP"), item.getString("VALUE"));
                                        }
            };
            
            ColorAction xtext = new ColorAction(types[i] +"_X",VisualItem.TEXTCOLOR) {
                public int getColor(VisualItem item) {
                    if (Props.getBoolean(Props.SHOW_X_AXIS_TEXT,false)) {
                        return Props.getColor(Props.Y_AXIS_TEXT_COLOR,super.getColor(item));
                    } else {
                        return ColorLib.rgba(255,255,255,255);
                    }
                }
            };
            
            AxisLabelLayout y = new AxisLabelLayout(types[i] + "_Y",Constants.Y_AXIS,ranges[i]);
            y.setSpacing(50);
            ActionList layout = new ActionList();
            layout.add(stroke);
            layout.add(fill);
            layout.add(stacks[i]);
            layout.add(y);
            layout.add(xranges[i]);
            layout.add(new ColorAction(types[i] + "_Y",VisualItem.STROKECOLOR,Props.getColor(Props.Y_AXIS_STROKE_COLOR)));
            layout.add(new ColorAction(types[i] + "_Y",VisualItem.TEXTCOLOR,Props.getColor(Props.Y_AXIS_TEXT_COLOR)));
            layout.add(xtext);
            layout.add(new RepaintAction());
            putAction(types[i],layout);
        }
    }
    
    public void setYears() {
        int start = Props.getInt(Props.CURRENT_RANGE_MIN,0);
        int stop = Props.getInt(Props.CURRENT_RANGE_MAX,Props.getInt(Props.NUMBER_OF_TERMS,0));
        System.out.println(DBRangeSlider.getTerm(start) + " to " + DBRangeSlider.getTerm(stop));
        String[] cols = new String[(stop-start)];
        String[] years = new String[stop-start];
        for (int i = start; i < stop; i++) {
            //System.out.println("I: " + i + " " + cols.length);
            cols[i-start] = DBRangeSlider.getTerm(i);
            years[i-start] = DBRangeSlider.getLabel(i);
            
        }
        for (int i = 0; i < types.length; i++) {
            stacks[i].setColumns(cols);
            xranges[i].setRangeModel(new ObjectRangeModel(years));
            if (years.length > 4) {
                Props.set(Props.SHOW_X_AXIS_TEXT,false);
            } else {
                Props.set(Props.SHOW_X_AXIS_TEXT,true);
            }
        }
        propertyChanged(new PropertyEvent(Props.SELECTED_ITEM,Props.get(Props.SELECTED_ITEM)));
    }
    
    public void propertyChanged(PropertyEvent evt) {
        if (evt.value != null) {
            if (evt.value instanceof Edge) {
                setEdgeGraphs((WeightTree)((Edge)evt.value).get(DatabookViz.TREE_COLUMN));
            } else if (evt.value instanceof Node) {
                setNodeGraphs((WeightTree)((Node)evt.value).get(DatabookViz.TREE_COLUMN));
            }
        }
    }
    
    public void setEdgeGraphs(WeightTree wt) {
        for (int i = 0; i < types.length; i++) {
            ranges[i].setHighValue(wt.getMaxWeight("TERM_TO",true));
            removeGroup(types[i]);
            addTable(types[i],wt.getStackedTable(types[i],"TERM_TO"));
            run(types[i]);
        }
    }
    
    public void setNodeGraphs(WeightTree wt) {
        for (int i = 0; i < types.length; i++) {
            ranges[i].setHighValue(wt.getMaxWeight("TERM",true));
            removeGroup(types[i]);
            addTable(types[i],wt.getStackedTable(types[i],"TERM"));
            run(types[i]);
        }
    }
    
    
    public VisualTable addTable(String group,Table table) {
        VisualTable vt = super.addTable(group,table);
        vt.addColumn(VisualItem.POLYGON,Object.class);
        vt.addColumn(PrefuseLib.getStartField(VisualItem.POLYGON),Object.class);
        vt.addColumn(PrefuseLib.getEndField(VisualItem.POLYGON),Object.class);
        return vt;
    }
}
